﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using MicroCenter.Models;

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

using Utils;

namespace MicroCenter.Services
{
    [Injection(ServiceLifetime.Singleton)]
    public class NginxService
    {
        public List<RegistryModel> OldRegistries;
        public readonly IWebHostEnvironment webHostEnvironment;

        public readonly IWritableOptions<NginxOptions> nginxOptions;

        private string NginxPath => !nginxOptions.Value.NginxPath.IsNullOrEmpty() ? "/usr/local/nginx" : (IsUbuntu() ? "/etc/nginx/" : "/usr/local/nginx");

        private string ConfigName = "micro-center.conf";

        #region 服务指令
        public bool IsRun()
        {
            return Process.GetProcessesByName("nginx").Any();
        }

        public void Start()
        {
            if (IsRun())
            {
                return;
            }
            string startCmd = nginxOptions.Value.StartCMD;
            if (string.IsNullOrEmpty(startCmd))
            {
                if (IsCentOS())
                {
                    ProcessHelper.RunCmd($"{NginxPath}/sbin/nginx").Start().GetAwaiter().GetResult();
                }
                else if (IsUbuntu())
                {
                    ProcessHelper.RunCmd($"service nginx start").Start().GetAwaiter().GetResult();
                }
            }
            else
            {
                ProcessHelper.RunCmd(startCmd).Start().GetAwaiter().GetResult();
            }
        }

        public void Stop()
        {
            string stopCmd = nginxOptions.Value.StopCMD;
            if (string.IsNullOrEmpty(stopCmd))
            {
                if (IsCentOS())
                {
                    ProcessHelper.RunCmd($"{NginxPath}/sbin/nginx -s stop").Start().GetAwaiter().GetResult();
                }
                else if (IsUbuntu())
                {
                    ProcessHelper.RunCmd($"service nginx stop").Start().GetAwaiter().GetResult();
                }
            }
            else
            {
                ProcessHelper.RunCmd(stopCmd).Start().GetAwaiter().GetResult();
            }
        }

        public void Reload()
        {

            if (!IsRun())
            {
                Start();
                return;
            }
            string reloadCmd = nginxOptions.Value.ReloadCMD;
            if (string.IsNullOrEmpty(reloadCmd))
            {
                if (IsCentOS())
                {
                    ProcessHelper.RunCmd($"{NginxPath}/sbin/nginx -s reload").Start().GetAwaiter().GetResult();
                }
                else if (IsUbuntu())
                {
                    ProcessHelper.RunCmd($"service nginx reload").Start().GetAwaiter().GetResult();
                }

            }
            else
            {
                ProcessHelper.RunCmd(reloadCmd).Start().GetAwaiter().GetResult();
            }
        }

        #endregion

        public void ReloadConfig(RegistryModel[] registryModels)
        {
            var pass = new List<string>();
            if (registryModels == null) return;
            if (OldRegistries != null)
            {

                if (OldRegistries.SequenceEqual(registryModels))
                {
                    return;
                }
                OldRegistries = registryModels.UnionBy(OldRegistries, a => a.ServiceName).ToList();
            }
            else
            {
                OldRegistries = registryModels.ToList();
            }

            foreach (var item in OldRegistries)
            {
                string serviceName = item.ServiceName.ToLower();
                if (item.ProxyUrl.IsNullOrEmpty())
                    continue;
                if (serviceName.StartsWith("api"))
                {
                    serviceName = serviceName.Substring(3);
                }
                if (serviceName.EndsWith("api"))
                {
                    serviceName = serviceName.Substring(serviceName.Length - 3);
                }
                pass.Add(BuildProxyPass($"/api/{serviceName}/", $"http://{item.RemoteIP}:{GetPort(item.ProxyUrl)}/"));
            }
            var value = nginxOptions.Value;
            string str = BuildService(value.Port, value.Hostname, value.Root, pass);
            string fullName = Path.Combine(webHostEnvironment.ContentRootPath, ConfigName);
            File.WriteAllText(fullName, str);
            Reload();
        }

        /// <summary>
        /// 植入配置
        /// </summary>
        public void InstallConfig()
        {
            string configPath = Path.Combine(NginxPath, IsUbuntu() ? "nginx.conf" : "conf/nginx.conf");

            if (!File.Exists(configPath))
            {
                throw Error.DefaultError("未找到配置文件");
            }
            string fullName = Path.Combine(webHostEnvironment.ContentRootPath, ConfigName);
            var textLines = File.ReadAllLines(configPath).ToList();
            if (textLines.Any(a => a.Contains(fullName)))
            {
                throw Error.DefaultError("已设置");
            }
            int index = textLines.FindIndex(a => a.Contains("http {"));
            if (index >= 0)
            {
                textLines.Insert(index + 1, $"    include  {fullName};");
            }
            File.WriteAllLines(configPath, textLines);
            Reload();
        }

        private string GetPort(string proxyUrl)
        {
            var url = proxyUrl.Split(';').FirstOrDefault(a => a.StartsWith("http://"));
            return url.Substring(7).Split(':').Last().TrimEnd('/');
        }

        private string BuildService(int port, string hostname, string rootPath, List<string> proxyPass)
        {
            return $@"
map $http_upgrade $connection_upgrade {{
  default upgrade;
  ''      close;
}}

     server {{
        listen       {port} ;
        server_name  {hostname};
        root   {rootPath};
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
{proxyPass.Join("\r\n")}
    }}";

        }

        private string BuildProxyPass(string path, string targetUrl)
        {
            return $@"
        location {path} {{
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_set_header  Host  $host;
            proxy_set_header  X-real-ip $remote_addr;
            proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass {targetUrl};
        }}";
        }


        private bool IsCentOS()
        {
            return GetRelease().Contains("CentOS");
        }

        private bool IsUbuntu()
        {
            return GetRelease().Contains("Ubuntu");
        }

        private string ReleaseName;
        private string GetRelease()
        {
            if (!ReleaseName.IsNullOrEmpty()) return ReleaseName;
            string release = File.ReadAllLines("/etc/os-release").FirstOrDefault(a => a.Contains("PRETTY_NAME="));
            if (string.IsNullOrWhiteSpace(release))
            {
                return "";
            }
            return ReleaseName = release.Replace("PRETTY_NAME=", "").Trim('"');
        }
    }
}
